package Statistics;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;

import java.io.Serializable;

/**
 * @author Xenophon
 * @version 2010-1-15 This class is similar to the IDGraph class except that it
 *          contains cluster or scc as its node, by this way, we can get a
 *          direct perspective of the graph structure.
 */
public class CompositeGraph implements Serializable{
	/**
	 * NODE_TYPE: 1 for SCC 2 for cluster
	 */
	private int graph_type = 1;

	public class CompositeGraphNode implements Serializable {
		private String id;
		private int size = 1;
		private HashMap<String, Integer> inEdges ;
		private HashMap<String, Integer> outEdges;

		public CompositeGraphNode(String id, int size) {
			this.id = id;
			this.size = size;
			inEdges = new HashMap<String, Integer>();
			outEdges = new HashMap<String, Integer>();
		}

		public int GetSize() {
			return size;
		}

		public String GetID() {
			return id;
		}

		public HashMap<String, Integer> GetInEdges() {
			return inEdges;
		}

		public HashMap<String, Integer> GetOutEdges() {
			return outEdges;
		}

		public void AddInEdge(String source, int weight) {
			if(inEdges.containsKey(source))
				   weight += inEdges.remove(source);
			inEdges.put(source, weight);
		}

		public void AddOutEdge(String sink, int weight) {
			if(outEdges.containsKey(sink))
			   weight += outEdges.remove(sink);
			outEdges.put(sink, weight);
		}
	}

	private HashMap<String, CompositeGraphNode> nodeMap;

	public CompositeGraph()
	{
		nodeMap = new HashMap<String, CompositeGraphNode>();
	}
	public CompositeGraph(Set<Set<String>> community, IDGraph graph, int type) {
		graph_type = type;
		int community_id = 1;
		HashMap<String, Integer> communityMap = new HashMap<String, Integer>();
		// We add all vertex firstly...
		for (Set<String> set : community) {
			for (String id : set)
				communityMap.put(id, community_id);

			CompositeGraphNode node = new CompositeGraphNode(
					GetIDbyID(community_id), set.size());
			nodeMap.put(GetIDbyID(community_id), node);
			community_id++;
		}

		Set<Entry<String, IDGraphNode>> entrySet = graph.GetNodeMap().entrySet();
		
		for (Map.Entry<String, IDGraphNode> entry : entrySet) {
			if (communityMap.containsKey(entry.getKey()))
				continue;
			CompositeGraphNode node = new CompositeGraphNode(entry.getKey(), 1);
			communityMap.put(entry.getKey(), 0);
			nodeMap.put(entry.getKey(), node);
		}
		// Now we can add edges to the graph...
		for (Map.Entry<String, IDGraphNode> entry : entrySet) {
			String id = entry.getKey();
			IDGraphNode nodeInTheIDGraph = entry.getValue();
			HashMap<String, Integer> outEdges = nodeInTheIDGraph.GetOutEdges();
			Set<Entry<String, Integer>> edgeSet = outEdges.entrySet();
			if (communityMap.get(id) != 0)
				id = GetIDbyID(communityMap.get(id));

			for (Map.Entry<String, Integer> edge : edgeSet) {
				String targetID = edge.getKey();
				int community_index = communityMap.get(targetID);
				int weight = edge.getValue();
				if (community_index != 0)
					targetID = GetIDbyID(community_index);
				AddEdge(id, targetID, weight);
			}
		}
	}

	public void AddEdge(String source, String target, int weight) {
		if(source.equals(target))
			return;
		System.out.println("Ready to Add "+source+" to "+target);
		if (!nodeMap.containsKey(source))
			return;
	    if (!nodeMap.containsKey(target))
			return;
		CompositeGraphNode sourceNode = nodeMap.remove(source);
		CompositeGraphNode targetNode = nodeMap.remove(target);
		try{
			if(sourceNode ==null)
				throw new Exception("SourceNode == null");
			if(targetNode == null)
				throw new Exception("targetNode == null");
		}catch (Exception e) {
			e.printStackTrace();
		}
		sourceNode.AddOutEdge(target, weight);
		targetNode.AddInEdge(source, weight);
		nodeMap.put(source, sourceNode);
		nodeMap.put(target, targetNode);
		System.out.println("Add Edge:　"+source+" : "+target);
	}

	protected String GetIDbyID(int id) {
		if (graph_type == 1)
			return "SCC " + id;
		else
			return "CLUSTER " + id;
	}
	
	public HashMap<String, CompositeGraphNode> GetNodeMap(){return nodeMap;}
	
	public void AddNode(String id, int size)
	{
		if(nodeMap.containsKey(id))
			return;
		CompositeGraphNode node = new CompositeGraphNode(id, size);
		nodeMap.put(id, node);
		System.out.println("Add Node :"+id);
	}
}
